package org.render;

import java.awt.Graphics;

import org.geom.Maths;
import org.geom.Matrix;
import org.geom.Point2D;
import org.geom.Point3D;


public abstract class Engine3D implements IRenderer {

	//---------------------------------------------------------------------- class vars
	// Parametri di visualizzazione
	protected float theta, // angolo orizzontale
			        phi;  // angolo verticale
	protected int rho;
	protected static int d;
	protected int u0,u1,	//(u0,v1)=angolo in alto a sinistra
			      v0,v1;   //(u1,v0)=angolo in basso a destra
	
	protected Point3D eye = new Point3D();
	protected Point3D prospective_center = new Point3D(); // centro di prospettiva.. 'at'??
		
	// Matrici di vista dell'algoritmo di Penna-Patterson ("Projective Geometry and its Application to Computer Graphics") a pag. 255
	protected float[][] M = new float[4][3];
	protected float[][] M12 = new float[4][3];
	protected float[][] M4 = new float[3][3];
	
	protected Point2D translation_offset = new Point2D();
	
	public static void setZoom(int n) {
		d = n;
	}
	public static int getZoom() {
		return d;
	}
	
	//---------------------------------------------------------------------- constructor
	public Engine3D() {
		init3Dstuffs();
	}
	
	private void init3Dstuffs() {
		d = 3760;
		rho = d + 25;
		
		theta = Maths.PI / 6;
        phi = Maths.PI / 2.5f;

		u0 = v1= 0;

  		translation_offset.x = 100;
  		translation_offset.y = 100; 
	}

	//---------------------------------------------------------------------- 3d engine core
	
	@Override
	public Point2D transform3dTo2d(Point3D p) {
		return transformCoordinateOnRotationMatrix(p);
	}

	public void drawString3D(String s, Point3D pin, Graphics g) {
		Point2D p = new Point2D();

		p = transformCoordinateOnRotationMatrix(pin);
		g.drawString(s, (int)p.x, (int)p.y);
	}
	
	public void drawLine3D(Point3D pin1, Point3D pin2, Graphics g) {
		Point2D p1 = new Point2D();
		Point2D p2 = new Point2D();
		
		p1 = transformCoordinateOnRotationMatrix(pin1);
		p2 = transformCoordinateOnRotationMatrix(pin2);
		
		g.drawLine((int)p1.x,(int)p1.y,
				   (int)p2.x,(int)p2.y);
	}
	
	protected Point2D transformCoordinateOnRotationMatrix(Point3D pin) {
		Point2D pout = new Point2D();
		float w = 0; //cos'è w?? TODO
		
		w = M[0][2]*pin.x + M[1][2]*pin.y + M[2][2]*pin.z + M[3][2];
		//w*= 0.6;

		pout.x = ((M[0][0]*pin.x + M[1][0]*pin.y + M[2][0]*pin.z + M[3][0]) /w)+ translation_offset.x;
		pout.y = ((M[0][1]*pin.x + M[1][1]*pin.y + M[2][1]*pin.z + M[3][1]) /w)+ translation_offset.y;

		return pout;
	}

	protected void updateMatrix(){
		
  		// Penna-Patterson Algorithm ("Projective Geometry and its Application to Computer Graphics")
		M12[0][0] = (d-rho) * Maths.sin(theta);
		M12[0][1] = (d-rho) * Maths.cos(phi) * Maths.cos(theta);
		M12[0][2] = - Maths.sin(phi) * Maths.cos(theta);
		M12[1][0] = (rho-d) * Maths.cos(theta);
		M12[1][1] = (d-rho) * Maths.cos(phi) * Maths.sin(theta);
		M12[1][2] = - Maths.sin(phi) * Maths.sin(theta);
		M12[2][0] = 0;
		M12[2][1] = (rho-d) * Maths.sin(phi);
		M12[2][2] = - Maths.cos(phi);
		M12[3][0] = 0;
		M12[3][1] = 0;
		M12[3][2] = rho;

  		M4[0][0] = (u1-u0)/2;
		M4[0][1] = 0;
		M4[0][2] = 0;
		M4[1][0] = 0;
		M4[1][1] = (v1-v0)/2;
		M4[1][2] = 0;
		M4[2][0] = (u1+u0)/2;
		M4[2][1] = (v1+v0)/2;
		M4[2][2] = 1;
		
  	 	M = Matrix.product(M12, M4);
		
		// Aggiorno il vettore di vista ( normalizzato )
		eye.x = Maths.sin(phi) * Maths.cos(theta);
		eye.y = Maths.sin(phi) * Maths.sin(theta);
		eye.z = Maths.cos(phi);

		prospective_center.x = eye.x * rho;
		prospective_center.y = eye.y * rho;
		prospective_center.z = eye.z * rho; 
	}

	//---------------------------------------------------------------------- getter && setter
	@Override
	public void setTranslate(int x, int y) {
		//System.out.println("Renderer3D > SetTranslate");
		this.translation_offset.x += x;
		this.translation_offset.y += y;
	}
	@Override
	public Point2D getTranslate() {
		return translation_offset;
	}

	@Override
	public void setRotate(int x, int y) {
		/* TODO : implements algorithm of peasy cam for rotation in all direction */
		float scaleFactor = Maths.PI/400;
		float deltaHorizontal = x*scaleFactor;
		float deltaVertical = y*scaleFactor;
		//System.out.println("Renderer3D > SetRotation");
		theta += deltaHorizontal;
		phi += deltaVertical;			
	}

	@Override
	public void setScale(int x, int y) {
		float deltaRho = y;//*0.5f; 
		rho += deltaRho;
		d = rho - 25;
		System.out.println("Renderer3D > SetScale "+rho);
	}

}
